home *** CD-ROM | disk | FTP | other *** search
- /*===================================================================
- *
- * FILE:
- * prs2bkwd.c
- *
- * IDENTIFICATION:
- * $Header: /private/postgres/src/rules/prs2/RCS/prs2bkwd.c,v 1.9 1992/07/14 00:22:42 mer Exp $
- *
- * DESCRIPTION:
- * These are routines that can be used to implement the
- * 'backward' chaining rules, i.e. rules that update the
- * current tuple. Examples of these rules are:
- * on retrieve to EMP.salary where EMP.name = "mike"
- * do instead retrieve (salary=1000)
- * or
- * on append to EMP where EMP.age<25
- * do replace CURRENT(salary=2000)
- *
- *
- * There are two main routines in this module:
- * `prs2ActivateBackwardChainingRules' and `prs2ActivateForwardChainingRules'
- *
- * At any point the rule manager has 1 or 2 tuples to consider. The 'old'
- * tuple and/or the 'new' tuple.
- * In the case of a 'delete' or 'retrieve' event, it only has an 'old'
- * tuple. In the case of a 'append' it only has a 'new' tuple, and finally
- * in the case of a 'replace' it has both a 'new' and an 'old' tuple.
- *
- * Any of these events can activate some rules. These rules can
- * either be 'backward chaining' rules (i.e. rules that have
- * a lock of type 'LockTypeXXXWrite' where XXX is the appropriate event)
- * or `forward chanining' (with a 'LockTypeXXXAction' lock).
- * In the first case, we must call 'prs2ActivateBackwardChainingRules' for
- * every attribute that has the 'LockTypeXXXWrite' lock.
- * This routine, finds these rules (if they exist) checks their
- * qualification and if true it activates them, retrieves the value
- * calculated by the rule and puts it in the tuple.
- *
- * Explanation of parameters:
- * Most fo them are common in both 'prs2ActivateBackwardChainingRules'
- * and 'prs2ActivateForwardChainingRules'.
- *
- * prs2EStateInfo: information (usually stored in the EState node)
- * needed for loop detection.
- * relation: the relation both 'old' & 'new' tuples belong to.
- * attributeNumber: the number of the attribute that is 'touched'
- * by the event.
- * oldOrNewTuple: is this an attribute of the 'old' or of the
- * 'new' tuple?
- * oldTupleOid: the oid of the 'old' tuple.
- * oldAttributeValues: an array containing the values of the
- * 'old' tuple, plus some other info.
- * oldTupleLocks: the rule locks of the old tuple.
- * oldTupleLockType: the lock type (one of the 'LockTypeXXXWrite'
- * locks that should be used to calculate the values of the
- * 'old' tuple's attributes. See discussion below.
- * newTupleOid,
- * newAttributeValues,
- * newTupleLocks,
- * newTupleLockType: similar to 'old' tuple stuff...
- *
- * The following arguments appear only in 'prs2ActivateForwardChainingRules'
- * eventLockType: The lock type that a forward chaining rule must
- * have in the specified attribute in order to be activated.
- *
- * Both routines, might call 'prs2CalculateAttributesOfParamNodes'
- * (which in turn might also call prs2ActivateBackwardChainingRules')
- * The reason is that a rule plan might have some parameters (the NEW
- * & OLD stuff...), in which case we have first to calculate the
- * correct values for the correpsonding attributes (of either the
- * 'new' or 'old' tuple, depending on the type of paramterer)
- * before we execute the plan.
- *
- * 'prs2ActivateBackwardChainingRules' is (directly) called by the PRS2
- * routines that handle 'retrieve', 'append' and 'replace' events.
- * It is called to activate all the backward chaining rules that
- * might affect the value of a certain attribute.
- *
- * In the 'retrieve' case, we only have an 'old' tuple, so all the
- * info about the 'new' tuple is ignored. The lock type that is
- * used is 'LockTypeRetrieveWrite'.
- * In the case of 'append' event, we only have a new tuple, and
- * the lock type is 'LockTypeAppendWrite'
- * Finally in the case of replace, we have both a 'new' & 'old'
- * tuple, however we (directly) call 'prs2ActivateBackwardChainingRules'
- * only for the new tuple and the lock type is 'LockTypeReplaceWrite'.
- *
- * 'prs2ActivateForwardChainingRules' is called by
- * the prs2 routines thath handle 'retrieve', 'delete', 'append'
- * and 'replace' events.
- *
- * In the 'retrieve' case, we only have a 'old' tuple, the
- * `eventTypeLock' is 'LockTypeRetrieveAction' and the 'oldTupleLockType'
- * is 'LockTypeRetrieveWrite'
- *
- * In the 'delete' case, we only have an 'old' tuple, the
- * `eventTypeLock' is 'LockTypeDeleteAction' and the 'oldTupleLockType'
- * is 'LockTypeRetrieveWrite'
- *
- * In the 'append' case, we only have a 'new' tuple, the
- * `eventTypeLock' is 'LockTypeAppendAction' and the 'newTupleLockType'
- * is 'LockTypeAppendWrite'
- *
- * In the 'replace' case, we have both an 'old' & a 'new' tuple, the
- * `eventTypeLock' is 'LockTypeReplaceAction', the 'oldTupleLockType'
- * is 'LockTypeRetrieveWrite' and the 'newTupleLockType' is
- * 'LockTypeReplaceWrite'
- *
- *===================================================================
- */
-
- #include "tmp/c.h"
- #include "utils/log.h"
- #include "rules/prs2.h"
-
- /*-------------------------------------------------------------------
- * prs2ActivateBackwardChainingRules()
- *
- */
- void
- prs2ActivateBackwardChainingRules(
- prs2EStateInfo,
- explainRelation,
- relation,
- attributeNumber,
- oldOrNewTuple,
- oldTupleOid,
- oldAttributeValues,
- oldTupleLocks,
- oldTupleLockType,
- newTupleOid,
- newAttributeValues,
- newTupleLocks,
- newTupleLockType,
- attributeArray,
- numberOfAttributes)
- Prs2EStateInfo prs2EStateInfo;
- Relation explainRelation;
- Relation relation;
- AttributeNumber attributeNumber;
- int oldOrNewTuple;
- ObjectId oldTupleOid;
- AttributeValues oldAttributeValues;
- RuleLock oldTupleLocks;
- Prs2LockType oldTupleLockType;
- ObjectId newTupleOid;
- AttributeValues newAttributeValues;
- RuleLock newTupleLocks;
- Prs2LockType newTupleLockType;
- AttributeNumberPtr attributeArray;
- AttributeNumber numberOfAttributes;
- {
- Prs2OneLock oneLock;
- int nlocks;
- int i;
- int finalStatus;
- LispValue plan;
- LispValue ruleInfo;
- LispValue planQual;
- LispValue planAction;
- ParamListInfo paramList;
- AttributeNumber lockAttrNo;
- AttributeValues attributeValues;
- Prs2LockType lockType;
- ObjectId ruleId;
- ObjectId tupleOid;
- RuleLock locks;
- Prs2LockType lockTypeToBeUsed;
- AttributeNumber eventAttr;
- AttributeNumber a;
- Boolean changed;
-
-
- if (oldOrNewTuple == PRS2_OLD_TUPLE) {
- if (oldAttributeValues == NULL) {
- elog(WARN,"prs2ActivateBackwardChainingRules: Internal error");
- }
- attributeValues = oldAttributeValues;
- lockTypeToBeUsed = oldTupleLockType;
- locks = oldTupleLocks;
- tupleOid = oldTupleOid;
- } else if (oldOrNewTuple == PRS2_NEW_TUPLE) {
- if (newAttributeValues == NULL) {
- elog(WARN,"prs2ActivateBackwardChainingRules: Internal error");
- }
- attributeValues = newAttributeValues;
- lockTypeToBeUsed = newTupleLockType;
- locks = newTupleLocks;
- tupleOid = newTupleOid;
- } else {
- elog(WARN,
- "prs2ActivateBackwardChainingRules: illegal tuple spec:%d",
- oldOrNewTuple);
- }
-
- if (attributeValues[attributeNumber-1].isCalculated) {
- /*
- * we have already calculated the value for this attribute.
- * Do nothing...
- */
- return;
- }
-
- nlocks = prs2GetNumberOfLocks(locks);
- for (i=0; i<nlocks ; i++) {
- oneLock = prs2GetOneLockFromLocks(locks, i);
- lockType = prs2OneLockGetLockType(oneLock);
- lockAttrNo = prs2OneLockGetAttributeNumber(oneLock);
- ruleId = prs2OneLockGetRuleId(oneLock);
- if (lockType==lockTypeToBeUsed&& attributeNumber==lockAttrNo) {
- /*
- * We've found a 'LockTypeXXXWrite' lock, i.e. there
- * is a rule that might calculate a new value
- * for this attribute.
- *
- * Check for loops!
- */
- if (prs2RuleStackSearch(prs2EStateInfo,
- ruleId, tupleOid, attributeNumber)) {
- /*
- * Ooops! a rule loop was found.
- * Currently we decide to ignore the rule.
- */
- continue; /* the for loop... */
- }
- /*
- * now add the stack (loop detection) info...
- */
- prs2RuleStackPush(prs2EStateInfo,
- ruleId, tupleOid, attributeNumber);
- /*
- * Go in the system catalogs and extract the
- * appropriate plan info...
- */
- plan = prs2GetRulePlanFromCatalog(
- ruleId,
- prs2OneLockGetPlanNumber(oneLock),
- ¶mList);
- if (null(plan)) {
- /*
- * No such plan has been found (an obsolete rule?)
- * Continue with the next lock...
- */
- prs2RuleStackPop(prs2EStateInfo);
- continue;
- }
- ruleInfo = prs2GetRuleInfoFromActionPlan(plan);
- planQual = prs2GetQualFromActionPlan(plan);
- planAction = prs2GetActionsFromActionPlan(plan);
-
- /*
- * in the case of a rule of the form:
- * ON REPLACE TO REL.Y THEN DO REPLACE NEW(X=...)
- * activate this rule only if the attribute 'Y' has been
- * replaced by either the user or another ON REPLACE rule.
- */
- if (lockType==LockTypeReplaceWrite
- && oldOrNewTuple == PRS2_NEW_TUPLE) {
- eventAttr = prs2GetEventAttributeNumberFromRuleInfo(ruleInfo);
- /*
- * try to calculate this attribute, in order to see if
- * it is calculated by another rule
- */
- prs2ActivateBackwardChainingRules(
- prs2EStateInfo,
- explainRelation,
- relation,
- eventAttr,
- PRS2_NEW_TUPLE,
- oldTupleOid,
- oldAttributeValues,
- oldTupleLocks,
- oldTupleLockType,
- newTupleOid,
- newAttributeValues,
- newTupleLocks,
- newTupleLockType,
- attributeArray,
- numberOfAttributes);
- /*
- * Now check if this attribute has been updated by
- * another rule (in which case its 'isChanged' field
- * would be True) or by the user (in which case this
- * attribute must be among the attributes of the
- * 'attributeArray'
- */
- changed = (Boolean) 0;
- if (numberOfAttributes == 0) {
- /*
- * that means 'all the attributes of the tuple'
- */
- changed = (Boolean) 1;
- }
- for (a=0; a<numberOfAttributes; a++) {
- if (attributeArray[a] == eventAttr) {
- changed = (Boolean) 1;
- break;
- }
- }
- if (attributeValues[eventAttr-1].isChanged)
- changed = (Boolean) 1;
- if (!changed) {
- /*
- * this attribute has not been updated by the user
- * or by another `on replace' rule.
- * So the current rule does not
- * apply, and we have to ignore it.
- */
- continue; /* the for loop... */
- }
- }
- /*
- * Now try to calculate all the parameters needed to fill in
- * the plan
- */
- prs2CalculateAttributesOfParamNodes(
- prs2EStateInfo,
- explainRelation,
- relation,
- paramList,
- oldTupleOid,
- oldAttributeValues,
- oldTupleLocks,
- oldTupleLockType,
- newTupleOid,
- newAttributeValues,
- newTupleLocks,
- newTupleLockType,
- attributeArray,
- numberOfAttributes);
- Prs2Stats_rulesTested +=1; /* update statistics */
- if (prs2CheckQual(planQual, paramList, prs2EStateInfo)) {
- Prs2Stats_rulesActivated +=1; /* update statistics */
- /*
- * the qualification is true. Execute the action
- * part in order to caclulate the new value
- * for the attribute...
- *
- * Note that 'actionPlan' must only contain one
- * item!
- */
- if (null(planAction)) {
- /*
- * then this is a
- * ON RETRIEVE TO ... DO INSTEAD NOTHING
- * rule.
- */
- attributeValues[attributeNumber-1].isCalculated= (Boolean)1;
- attributeValues[attributeNumber-1].isChanged= (Boolean)1;
- attributeValues[attributeNumber-1].isNull= (Boolean)1;
- } else {
- if (CDR(planAction) != LispNil) {
- elog(WARN, "planAction has more than 1 items!");
- }
- /*
- * this is NOT a null action
- */
- if (prs2RunOnePlanAndGetValue(CAR(planAction),
- paramList,
- prs2EStateInfo,
- &(attributeValues[attributeNumber-1].value),
- &(attributeValues[attributeNumber-1].isNull))) {
- /*
- * A value was calculated by the rule.
- * Store it in the 'attributeValues' array.
- */
- attributeValues[attributeNumber-1].isCalculated=
- (Boolean)1;
- attributeValues[attributeNumber-1].isChanged=
- (Boolean)1;
- if (explainRelation != NULL) {
- storeExplainInfo(explainRelation,
- ruleId,
- relation,
- tupleOid);
- }
- }/*else*/
- }
- /*
- * OK, now pop the rule stack information
- */
- prs2RuleStackPop(prs2EStateInfo);
- }
- }
- }/*for*/
-
- if (!attributeValues[attributeNumber-1].isCalculated) {
- /*
- * no rule was applicable. Use the value stored in the tuple
- * (NOTE: this value has already been stored in the attributeValues,
- * (when the 'attributeValues' array was first created)
- * so we only need to change the flags.
- */
- attributeValues[attributeNumber-1].isCalculated = (Boolean)1;
- attributeValues[attributeNumber-1].isChanged = (Boolean)0;
- }
-
- return;
-
- }
-
- /*----------------------------------------------------------------------
- *
- * prs2CalculateAttributesOfParamNodes
- *
- */
-
- void
- prs2CalculateAttributesOfParamNodes(
- prs2EStateInfo,
- explainRelation,
- relation,
- paramList,
- oldTupleOid,
- oldTupleAttributeValues,
- oldTupleRuleLock,
- oldTupleLockType,
- newTupleOid,
- newTupleAttributeValues,
- newTupleRuleLock,
- newTupleLockType,
- attributeArray,
- numberOfAttributes)
- Prs2EStateInfo prs2EStateInfo;
- Relation explainRelation;
- Relation relation;
- ParamListInfo paramList;
- ObjectId oldTupleOid;
- AttributeValues oldTupleAttributeValues;
- RuleLock oldTupleRuleLock;
- Prs2LockType oldTupleLockType;
- ObjectId newTupleOid;
- AttributeValues newTupleAttributeValues;
- RuleLock newTupleRuleLock;
- Prs2LockType newTupleLockType;
- AttributeNumberPtr attributeArray;
- AttributeNumber numberOfAttributes;
- {
-
- Name attributeName;
- AttributeNumber attributeNumber;
- LispValue attributesNeededInPlan;
- LispValue lispAttributeName;
- int i;
- Boolean isNull;
-
- if (paramList == NULL) {
- return;
- }
-
- i = 0;
- while (paramList[i].kind != PARAM_INVALID) {
- attributeName = paramList[i].name;
- attributeNumber = paramList[i].id;
- if (paramList[i].kind == PARAM_OLD) {
- /*
- * make sure that the value stored in the old tuple
- * is the correct one
- */
- prs2ActivateBackwardChainingRules(
- prs2EStateInfo,
- explainRelation,
- relation,
- attributeNumber,
- PRS2_OLD_TUPLE,
- oldTupleOid,
- oldTupleAttributeValues,
- oldTupleRuleLock,
- oldTupleLockType,
- newTupleOid,
- newTupleAttributeValues,
- newTupleRuleLock,
- newTupleLockType,
- attributeArray,
- numberOfAttributes);
- /*
- * Find and store the value for this parameter.
- */
- if (attributeNumber > 0 ) {
- paramList[i].value =
- oldTupleAttributeValues[attributeNumber-1].value;
- if (oldTupleAttributeValues[attributeNumber-1].isNull)
- paramList[i].isnull = true;
- else
- paramList[i].isnull = false;
- } else {
- if (attributeNumber == ObjectIdAttributeNumber){
- paramList[i].value = ObjectIdGetDatum(oldTupleOid);
- } else {
- elog(WARN,
- "Sorry,no system attributes (except oid) allowed in rules");
- }
- }
- } else if (paramList[i].kind == PARAM_NEW) {
- /*
- * make sure that the value stored in the new tuple
- * is the correct one
- */
- prs2ActivateBackwardChainingRules(
- prs2EStateInfo,
- explainRelation,
- relation,
- attributeNumber,
- PRS2_NEW_TUPLE,
- oldTupleOid,
- oldTupleAttributeValues,
- oldTupleRuleLock,
- oldTupleLockType,
- newTupleOid,
- newTupleAttributeValues,
- newTupleRuleLock,
- newTupleLockType,
- attributeArray,
- numberOfAttributes);
- /*
- * store information about this attribute (type, value etc)
- * in the paramList
- */
- paramList[i].type = get_atttype(relation->rd_id, attributeNumber);
- paramList[i].length = (Size) get_typlen(paramList[i].type);
- if (attributeNumber > 0) {
- paramList[i].value =
- newTupleAttributeValues[attributeNumber-1].value;
- if (newTupleAttributeValues[attributeNumber-1].isNull)
- paramList[i].isnull = true;
- else
- paramList[i].isnull = false;
- } else {
- if (attributeNumber == ObjectIdAttributeNumber){
- paramList[i].value = ObjectIdGetDatum(newTupleOid);
- } else {
- elog(WARN,
- "Sorry,no system attributes (except oid) allowed in rules");
- }
- }
- } else {
- elog(WARN,"Illegal param kind = (%d), name = %s",
- paramList[i].kind, paramList[i].name);
- }
- /*
- * Continue with the next parameter
- */
- i += 1;
- } /*while*/
- }
-
- /*-----------------------------------------------------------------------
- * prs2ActivateForwardChainingRules
- */
- void
- prs2ActivateForwardChainingRules(
- prs2EStateInfo,
- explainRelation,
- relation,
- attributeNumber,
- actionLockType,
- oldOrNewTuple,
- oldTupleOid,
- oldAttributeValues,
- oldTupleLocks,
- oldTupleLockType,
- newTupleOid,
- newAttributeValues,
- newTupleLocks,
- newTupleLockType,
- insteadRuleFoundP,
- attributeArray,
- numberOfAttributes)
- Prs2EStateInfo prs2EStateInfo;
- Relation explainRelation;
- Relation relation;
- AttributeNumber attributeNumber;
- int oldOrNewTuple;
- Prs2LockType actionLockType;
- ObjectId oldTupleOid;
- AttributeValues oldAttributeValues;
- RuleLock oldTupleLocks;
- Prs2LockType oldTupleLockType;
- ObjectId newTupleOid;
- AttributeValues newAttributeValues;
- RuleLock newTupleLocks;
- Prs2LockType newTupleLockType;
- bool *insteadRuleFoundP;
- AttributeNumberPtr attributeArray;
- AttributeNumber numberOfAttributes;
- {
- Prs2OneLock oneLock;
- int nlocks;
- int i;
- int finalStatus;
- LispValue plan;
- LispValue ruleInfo;
- LispValue planQual;
- LispValue planActions;
- ParamListInfo paramList;
- AttributeNumber lockAttrNo;
- Prs2LockType lockType;
- ObjectId ruleId;
- RuleLock locks;
- AttributeValues attributeValues;
- ObjectId tupleOid;
-
- /*
- * Have we encountered an 'instead' rule ???
- */
- *insteadRuleFoundP = false;
-
- if (oldOrNewTuple == PRS2_OLD_TUPLE) {
- attributeValues = oldAttributeValues;
- locks = oldTupleLocks;
- tupleOid = oldTupleOid;
- } else if (oldOrNewTuple == PRS2_NEW_TUPLE) {
- attributeValues = newAttributeValues;
- locks = newTupleLocks;
- tupleOid = newTupleOid;
- } else {
- elog(WARN,
- "prs2ActivateForwardChainingRules: illegal tuple spec:%d",
- oldOrNewTuple);
- }
-
- nlocks = prs2GetNumberOfLocks(locks);
- for (i=0; i<nlocks ; i++) {
- oneLock = prs2GetOneLockFromLocks(locks, i);
- lockType = prs2OneLockGetLockType(oneLock);
- lockAttrNo = prs2OneLockGetAttributeNumber(oneLock);
- ruleId = prs2OneLockGetRuleId(oneLock);
- if (lockType==actionLockType && attributeNumber==lockAttrNo) {
- /*
- * We've found a 'LockTypeXXXAction' lock, i.e. there
- * is a rule that might be activated by this event.
- * for this attribute.
- *
- * Go in the system catalogs and extract the
- * appropriate plan info...
- */
- plan = prs2GetRulePlanFromCatalog(
- ruleId,
- prs2OneLockGetPlanNumber(oneLock),
- ¶mList);
- if (null(plan)) {
- /*
- * No such plan has been found (an obsolete rule?)
- * Continue with the next lock...
- */
- continue;
- }
- ruleInfo = prs2GetRuleInfoFromActionPlan(plan);
- planQual = prs2GetQualFromActionPlan(plan);
- planActions = prs2GetActionsFromActionPlan(plan);
-
- /*
- * Now try to calculate all the parameters needed to fill in
- * the plan
- */
- prs2CalculateAttributesOfParamNodes(
- prs2EStateInfo,
- explainRelation,
- relation,
- paramList,
- oldTupleOid,
- oldAttributeValues,
- oldTupleLocks,
- oldTupleLockType,
- newTupleOid,
- newAttributeValues,
- newTupleLocks,
- newTupleLockType,
- attributeArray,
- numberOfAttributes);
- Prs2Stats_rulesTested +=1; /* update statistics */
- if (prs2CheckQual(planQual, paramList, prs2EStateInfo)) {
- /*
- * the qualification is true. Execute the action
- * part of the rule.
- */
- Prs2Stats_rulesActivated +=1; /* update statistics */
- prs2RunActionPlans(planActions, paramList, prs2EStateInfo);
- if (prs2IsRuleInsteadFromRuleInfo(ruleInfo)) {
- *insteadRuleFoundP = true;
- }
- if (explainRelation != NULL) {
- storeExplainInfo(explainRelation,
- ruleId,
- relation,
- tupleOid);
- }
- }
- }
- }/*for*/
- }
-